package nl.infodation.emr.pricingservice.controller;

import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import nl.infodation.emr.pricingservice.data.DomainRuleSearchItem;
import nl.infodation.emr.pricingservice.data.ResourceRepository;
import nl.infodation.emr.pricingservice.entity.DomainRule;
import nl.infodation.emr.pricingservice.entity.Environment;
import nl.infodation.emr.pricingservice.entity.SystemValue;
import nl.infodation.emr.pricingservice.ui.DomainRuleList;
import nl.infodation.emr.pricingservice.ui.page.SessionData;
import nl.infodation.emr.utility.Constants;
import nl.infodation.emr.utility.StringUtil;

import com.vaadin.data.util.BeanItem;

@SuppressWarnings({ "unchecked", "rawtypes", "deprecation" })
public class DomainRuleController extends
		AbstractBaseController<DomainRule, DomainRuleSearchItem, Long> {
	
	public long existsRuleName(String ruleName) {
		EntityManager entityManager = null;
		List<DomainRule> domainRules = null;
		try {
			entityManager = EM_FACTORY.createEntityManager();
			String hql = "SELECT d FROM DomainRule d WHERE d.ruleName = :ruleName";
			Query query = entityManager.createQuery(hql);
			query.setParameter("ruleName", ruleName);
			domainRules = query.getResultList();

		} catch (IllegalStateException illegalStateException) {
			LOG.error("Cannot search rule name", illegalStateException);
			rollback(entityManager);
		} catch (IllegalArgumentException illegalArgumentException) {
			LOG.error("Cannot search rule name", illegalArgumentException);
			rollback(entityManager);
		} finally {
			close(entityManager);
		}
		return domainRules != null ? domainRules.size() : 0;
	}

	@Override
	public long getTotalNumberItemByCriteria(DomainRuleSearchItem t) {
		long countResult = 0l;
		EntityManager entityManager = null;
		try {
			entityManager = EM_FACTORY.createEntityManager();
			entityManager.getTransaction().begin();
			queryString.setLength(0);
			final DomainRuleSearchItem item = (DomainRuleSearchItem) t;

			// Process for role
			String roleName = SessionData.getRoleName();
			if (Constants.PROVEA_ROLE.equals(roleName)) {
				item.setBusLine(Constants.PROVEA_ROLE);
			} else if (Constants.AFM_ROLE.equals(roleName)) {
				item.setBusLine(Constants.AFM_ROLE);
			}

			final DomainRule p = new DomainRule();
			if (!StringUtil.isEmptyOrBlank(item.getPromoCode())) {
				p.setPromotionValue(item.getPromoCode());
			}

			if (!StringUtil.isEmptyOrBlank(item.getRuleName())) {
				p.setRuleName(item.getRuleName());
			}

			if (!StringUtil.isEmptyOrBlank(item.getCountry())) {
				p.setCountry(item.getCountry());
			}

			if (!StringUtil.isEmptyOrBlank(item.getBusLine())) {
				p.setBusLine(item.getBusLine());
			}

			if (!StringUtil.isEmptyOrBlank(item.getLanguage())) {
				p.setLanguageValue(item.getLanguage());
			}

			Environment envir = null;
			IController iController = ControllerFactory
					.getEnvironmentController();
			envir = (Environment) iController.getObjectById(SessionData
					.getEnvironment());

			if (envir != null) {
				p.setEnvironment(envir);

				buildSelectCount(p);
				buildQueryAndParams(p);
				buildExtraQuery(item.getFromDate(), item.getToDate(),
						item.getTodayDate());
				buildUserRightsToSeeRuleWithCountryCode();
				// select active Rule
				if (item.getPromotionActive() != null) {
					buildActiveDomainRuleQuery(item.getPromotionActive());
				}
				final TypedQuery<Long> query = entityManager.createQuery(
						queryString.toString(), Long.class);
				LOG.info("Executing the query \"{}\".", queryString);
				countResult = query.getSingleResult();
			}
			entityManager.getTransaction().commit();
		} catch (IllegalStateException | IllegalAccessException ex) {
			LOG.error("Error reading getTotalNumberItemByCriteria", ex);
			if (entityManager != null
					&& entityManager.getTransaction().isActive()) {
				LOG.info("Rolling back...");
				rollback(entityManager);
			}
		} finally {
			close(entityManager);
		}
		return countResult;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see nl.infodation.emr.pricingservice.controller.AbstractBaseController#
	 * getObjectById(java.lang.Object)
	 */
	@Override
	public DomainRule getObjectById(Long objectId) {
		DomainRule obj = null;
		EntityManager entityManager = null;

		try {
			entityManager = EM_FACTORY.createEntityManager();

			obj = entityManager.find(DomainRule.class, objectId);
			DomainRule p = (DomainRule) obj;
			// Process for role
			String roleName = SessionData.getRoleName();
			if (Constants.PROVEA_ROLE.equals(roleName)) {
				if (!p.getBusLine().equals(Constants.PROVEA_ROLE)) {
					LOG.error(
							"Error getting domain rule! the business line must be \"{}\".",
							Constants.PROVEA_ROLE);
					return null; // failed
				}
			} else if (Constants.AFM_ROLE.equals(roleName)) {
				if (!p.getBusLine().equals(Constants.AFM_ROLE)) {
					LOG.error(
							"Error getting domain rule! the business line must be \"{}\".",
							Constants.AFM_ROLE);
					return null; // failed
				}
			}
		} catch (IllegalStateException ex) {
			LOG.error("Error reading getObjectById", ex);
		} finally {
			if (entityManager != null && entityManager.isOpen()) {
				entityManager.close();
			}
		}
		return obj;
	}

	/**
	 * @param ruleName
	 * @param environment
	 * @return
	 */
	public DomainRule getRuleByName(String ruleName, int environment) {
		DomainRule obj = null;
		EntityManager entityManager = null;
		try {
			entityManager = EM_FACTORY.createEntityManager();
			String ruleQuery = "select r from DomainRule r where r.ruleName = '"
					+ ruleName.replace("'", "''")
					+ "' and r.environment = "
					+ environment;
			Query q = entityManager.createQuery(ruleQuery);
			List rules = q.getResultList();
			if (rules.size() == 1) {
				DomainRule p = (DomainRule) rules.get(0);
				if (p != null) {
					obj = p;
					// Process for role
					String roleName = SessionData.getRoleName();
					if (Constants.PROVEA_ROLE.equals(roleName)) {
						if (!p.getBusLine().equals(Constants.PROVEA_ROLE)) {
							return null; // failed
						}
					} else if (Constants.AFM_ROLE.equals(roleName)) {
						if (!p.getBusLine().equals(Constants.AFM_ROLE)) {
							return null; // failed
						}
					}
				}
			}
		} catch (IllegalStateException ex) {
			LOG.error("Error reading getRuleByName", ex);
		} finally {
			close(entityManager);
		}
		return obj;
	}

	private String getBusinessLineValue() {
		String roleName = SessionData.getRoleName();
		if (SessionData.isSuperAdmin()) {
			return SessionData.getDeploymentRoleName();
		}
		// PROVEA or AFM
		return roleName;
	}

	/**
	 * @param em
	 * @param environmentId
	 * @param businessLine
	 * @return
	 */
	public List<DomainRule> getRulesForBusinessLineEnvironment(
			EntityManager em, int environmentId, String businessLine) {
		List<DomainRule> rules = null;
		CriteriaBuilder cb = em.getCriteriaBuilder();
		CriteriaQuery<DomainRule> q = cb.createQuery(DomainRule.class);
		Root<DomainRule> r = q.from(DomainRule.class);
		Join<DomainRule, Environment> e = r.join("environment", JoinType.LEFT);
		Predicate bl = cb.equal(r.get("busLine"), businessLine);
		Predicate env = cb.equal(e.get("environmentId"), environmentId);
		Predicate pAnd = cb.and(bl, env);
		q.where(pAnd);
		TypedQuery<DomainRule> rq = em.createQuery(q);
		rules = rq.getResultList();
		return rules;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see nl.infodation.emr.pricingservice.controller.AbstractBaseController#
	 * copyToEnvironment(int, boolean)
	 */
	public boolean copyToEnvironment(int environmentId, boolean clean) {
		boolean copied = Boolean.FALSE;
		EntityManager entityManager = null;
		try {
			entityManager = EM_FACTORY.createEntityManager();
			entityManager.getTransaction().begin();
			if (clean) {
				LOG.info("Cleaning rules in environment..." + environmentId);
				String businessLine = this.getBusinessLineValue();
				// Collect all rules for this business line and environment
				List<DomainRule> rules2Del = this
						.getRulesForBusinessLineEnvironment(entityManager,
								environmentId, businessLine);
				LOG.info(rules2Del.size() + " rules found in environment..."
						+ environmentId);
				for (DomainRule r2d : rules2Del) {
					entityManager.remove(r2d);
				}
			}
			LOG.info("Copying rules to environement..." + environmentId);
			int counter = 0;
			for (long promotionId : DomainRuleList.getActiveList()) {
				DomainRule p = (DomainRule) getObjectById(promotionId);
				// Process for role
				String roleName = SessionData.getRoleName();
				if (Constants.PROVEA_ROLE.equals(roleName)) {
					if (!p.getBusLine().equals(Constants.PROVEA_ROLE)) {
						continue; // skip
					}
				} else if (Constants.AFM_ROLE.equals(roleName)) {
					if (!p.getBusLine().equals(Constants.AFM_ROLE)) {
						continue; // skip
					}
				}
				// copy
				counter++;
				DomainRule prodP = p.copy();
				prodP.setDomainRuleId(null);
				Environment envir = null;
				IController iController = ControllerFactory
						.getEnvironmentController();
				envir = (Environment) iController.getObjectById(environmentId);
				if (envir == null) {
					LOG.error("Error saving domain rule.");

				} else {
					prodP.setEnvironment(envir);
					copied = saveObject(prodP);
				}
			}
			entityManager.getTransaction().commit();
			LOG.info(counter + " rules copied to environement..."
					+ environmentId);
		} catch (IllegalStateException ex) {
			LOG.error("Error copyToEnvironment", ex);
			if (entityManager != null
					&& entityManager.getTransaction().isActive()) {
				LOG.info("Rolling back...");
				rollback(entityManager);
			}

		} finally {
			close(entityManager);
		}
		
		if (copied) {
			LOG.info("Domain rules are copied.");
		}
		return copied;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see nl.infodation.emr.pricingservice.controller.AbstractBaseController#
	 * getPaginationByCriteria(java.lang.Object, int, int)
	 */
	@Override
	public List<BeanItem> getPaginationByCriteria(DomainRuleSearchItem object,
			int startPage, int pageSize) {
		final List<BeanItem> beans = new ArrayList<BeanItem>();
		EntityManager entityManager = null;
		try {
			entityManager = EM_FACTORY.createEntityManager();

			queryString.setLength(0);
			final DomainRuleSearchItem item = object;

			// Process for role
			String roleName = SessionData.getRoleName();
			if (Constants.PROVEA_ROLE.equals(roleName)) {
				item.setBusLine(Constants.PROVEA_ROLE);
			} else if (Constants.AFM_ROLE.equals(roleName)) {
				item.setBusLine(Constants.AFM_ROLE);
			}

			final DomainRule p = new DomainRule();
			if (!StringUtil.isEmptyOrBlank(item.getPromoCode())) {
				p.setPromotionValue(item.getPromoCode());
			}

			if (!StringUtil.isEmptyOrBlank(item.getRuleName())) {
				p.setRuleName(item.getRuleName());
			}

			if (!StringUtil.isEmptyOrBlank(item.getCountry())) {
				p.setCountry(item.getCountry());
			}

			if (!StringUtil.isEmptyOrBlank(item.getBusLine())) {
				p.setBusLine(item.getBusLine());
			}

			if (!StringUtil.isEmptyOrBlank(item.getLanguage())) {
				p.setLanguageValue(item.getLanguage());
			}

			Environment envir = null;
			IController iController = ControllerFactory
					.getEnvironmentController();
			envir = (Environment) iController.getObjectById(SessionData
					.getEnvironment());

			if (envir != null) {
				p.setEnvironment(envir);
				buildSelect(p);
				buildQueryAndParams(p);
				buildExtraQuery(item.getFromDate(), item.getToDate(),
						item.getTodayDate());
				buildUserRightsToSeeRuleWithCountryCode();
				// select active Rule				
				buildActiveDomainRuleQuery(Boolean.TRUE);
				
				final TypedQuery<DomainRule> query = entityManager.createQuery(
						queryString.toString(), DomainRule.class);
				if (startPage > 0) {
					query.setFirstResult((startPage - 1) * pageSize);
				} else {
					query.setFirstResult(1);
				}
				query.setMaxResults(pageSize);
				LOG.info("Executing the query \"{}\".", queryString);
				final List<DomainRule> results = query.getResultList();
				if (!results.isEmpty()) {
					for (DomainRule domainRule : results) {
						beans.add(new BeanItem<DomainRule>(domainRule));
					}
				}
			}

		} catch (IllegalStateException | IllegalAccessException ex) {
			LOG.error("Error reading getPaginationByCriteria.", ex);
		} finally {
			close(entityManager);
		}
		return beans;
	}

	private void buildActiveDomainRuleQuery(boolean active) {
		if (!queryString.toString().toLowerCase().contains("where")) {
			append(" WHERE ");
		} else {
			append(" AND ");
		}
		append(queryObjChar + "promotionActive = " + active);

	}

	private void buildUserRightsToSeeRuleWithCountryCode() {
		if (SessionData.isSuperAdmin()) {
			return;
		}

		if (null != SessionData.getCountriesToSee()) {
			Iterator<SystemValue> iterator = SessionData.getCountriesToSee()
					.iterator();

			if (!queryString.toString().toLowerCase().contains("where")) {
				append(" WHERE ");
			} else {
				if (iterator.hasNext()) {
					append(" AND ( ");
					append(queryObjChar + "country LIKE '%"
							+ iterator.next().getSystemCode() + "%'");
					while (iterator.hasNext()) {
						append(" or ");
						append(queryObjChar + "country LIKE '%"
								+ iterator.next().getSystemCode() + "%'");
					}
					append(" ) ");
				}
			}
		}
	}

	private void buildExtraQuery(Date fromDate, Date toDate, Date todayDate) {
		if (fromDate != null) {

			if (!queryString.toString().toLowerCase().contains("where")) {
				append(" WHERE ");
			}

			final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd",
					Locale.US);
			final String fromValue = format.format(fromDate);
			append(" AND ");
			append("(");
			append(queryObjChar + "endDate" + "  >= '" + fromValue
					+ " 00:00:00'");
			append(" OR ");
			append(queryObjChar + "endDate IS NULL");
			append(")");
		}

		if (toDate != null) {
			if (!queryString.toString().toLowerCase().contains("where")) {
				append(" WHERE ");
			}

			final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd",
					Locale.US);
			final String toValue = format.format(toDate);
			append(" AND ");
			append("(");
			append(queryObjChar + "endDate" + " <= '" + toValue + " 23:59:59'");
			append(" OR ");
			append(queryObjChar + "endDate IS NULL");
			append(")");
		}

		if (todayDate != null) {
			if (!queryString.toString().toLowerCase().contains("where")) {
				append(" WHERE ");
			}

			final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd",
					Locale.US);
			final String todayValue = format.format(todayDate);
			append(" AND ");
			append("(");
			append(queryObjChar + "endDate" + "  >= '" + todayValue
					+ " 00:00:00'");

			append(" OR ");
			append(queryObjChar + "endDate IS NULL");
			append(")");
		}
	}

	@Override
	public boolean delete(DomainRule object) {
		return Boolean.FALSE;
	}

	@Override
	public boolean saveObject(DomainRule object) {
		boolean saved = Boolean.FALSE;
		EntityManager entityManager = null;
		try {
			entityManager = EM_FACTORY.createEntityManager();
			entityManager.getTransaction().begin();

			final DomainRule item = (DomainRule) object;

			// Process for role
			String roleName = SessionData.getRoleName();
			if (Constants.PROVEA_ROLE.equals(roleName)) {
				if (!item.getBusLine().equals(Constants.PROVEA_ROLE)) {
					LOG.error(
							"Error saving! the business line must be \"{}\".",
							Constants.PROVEA_ROLE);
					return Boolean.FALSE; // failed
				}
			} else if (Constants.AFM_ROLE.equals(roleName)) {
				if (!item.getBusLine().equals(Constants.AFM_ROLE)) {
					LOG.error(
							"Error saving! the business line must be \"{}\".",
							Constants.AFM_ROLE);
					return Boolean.FALSE; // failed
				}
			}

			if (item.getDomainRuleId() != null) { /* update promotion */
				LOG.info("Updating domain rule...");
				entityManager.merge(item);
			} else { /* add promotion */
				LOG.info("Inserting domain rule...");
				final DomainRule domainRule = item.copyWithoutKey();
				entityManager.persist(domainRule);
			}

			entityManager.getTransaction().commit();
			saved = Boolean.TRUE;
		} catch (IllegalStateException ex) {
			LOG.error("Error saving domain rule.", ex);
			if (entityManager != null
					&& entityManager.getTransaction().isActive()) {
				LOG.info("Rolling back...");
				rollback(entityManager);
			}
		} finally {
			close(entityManager);
		}

		if (saved) {
			LOG.info("Domain rule is saved.");
		}
		return saved;
	}

	@Override
	public List<BeanItem> getPaginationList(final int startPage,
			final int pageSize) {
		return getPaginationByCriteria(new DomainRuleSearchItem(), startPage,
				pageSize);
	}

	@Override
	public long getTotalNumberItem() {
		return getTotalNumberItemByCriteria(new DomainRuleSearchItem());
	}

	@Override
	protected Boolean isObject(Field field) {
		if (field.getName().equals("environment")) {
			return Boolean.TRUE;
		}
		return Boolean.FALSE;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * nl.infodation.emr.pricingservice.controller.AbstractBaseController#deleteById
	 * (java.lang.Object)
	 */
	@Override
	public boolean deleteById(Long id) {
		boolean deleted = Boolean.FALSE;
		EntityManager entityManager = null;

		try {
			entityManager = EM_FACTORY.createEntityManager();
			entityManager.getTransaction().begin();

			String queryText = ResourceRepository.getCommonSQL().get(
					Constants.DELETE_DOMAIN_RULE_BY_ID);

			if (queryText == null) {
				LOG.error("Query is not set in CommonSQL.properties.");

			} else {
				final Query query = entityManager.createQuery(queryText);
				query.setParameter("id", id);
				LOG.info("Executing the query \"{}\".", queryText);
				query.executeUpdate();
				deleted = Boolean.TRUE;
			}

			entityManager.getTransaction().commit();
		} catch (IllegalStateException ex) {
			LOG.error("Error deleteById.", ex);
			if (entityManager != null
					&& entityManager.getTransaction().isActive()) {
				LOG.info("Rolling back...");
				rollback(entityManager);
			}
		} finally {
			close(entityManager);
		}
		if (deleted) {
			LOG.info("Domain rule is deleted.");
		}
		return deleted;
	}

	@Override
	protected DomainRule buildCriteria(DomainRuleSearchItem search) {
		// TODO Auto-generated method stub
		return null;
	}

}
